# coding=utf-8
"""
author: jamon
"""

from __future__ import annotations
import pygame
import weakref
from pygame.sprite import Group

from client.model.entity_base import Buff, EntityBase
from client.model.skill.skill import Skill
from client.model.tank.tank_base import TankBase
from client.model.weapon.bullet import Bullet

from package.static.const import Faction, MoveDirection, TypeIdConst
from typing import Optional, TYPE_CHECKING

from package.universe import universe

if TYPE_CHECKING:
    from client.views.battle_scene import BattleScreen


class CollideHandler(object):
    """"碰撞处理"""

    def __init__(self, battle_screen: BattleScreen):
        self.scene: BattleScreen = weakref.proxy(battle_screen, self.on_scene_destroy)

    def on_scene_destroy(self, scene):
        pass

    def _tank_move_handler(self, faction: Faction):
        """坦克碰撞处理"""
        same_tanks: Optional[Group] = None
        other_tanks: Optional[Group] = None
        if Faction.OUR_FACTION == faction:
            same_tanks = self.scene.our_tanks
            other_tanks = self.scene.npc_tanks
        elif Faction.NPC == faction:
            same_tanks = self.scene.npc_tanks
            other_tanks = self.scene.our_tanks
        if same_tanks:
            for tank in same_tanks:
                if isinstance(tank, TankBase) and not tank.is_dead:
                    tank.save_cur_scene()
                    if tank.next_direction:
                        # 如果有下一个方向，先尝试朝下一个方向移动，不行再尝试走当前方向
                        # print("before next_dir try: tank={}".format(tank))
                        has_move = tank.try_move_next()
                        # print("after next_dir try: can_move={}, tank={}".format(has_move, tank))
                        if has_move:
                            is_real_move = True
                            collide_tanks = []
                            # 判断是否和异势力坦克碰撞
                            collide_npc = pygame.sprite.spritecollide(tank, other_tanks, False, None)
                            collide_tanks.extend(collide_npc)

                            # 判断是否和同势力其他坦克碰撞
                            same_tanks.remove(tank)
                            collide_friend = pygame.sprite.spritecollide(tank, same_tanks, False, None)
                            same_tanks.add(tank)
                            collide_tanks.extend(collide_friend)

                            for i in range(len(collide_tanks)-1, -1):
                                c_tank = collide_tanks[i]
                                if isinstance(c_tank, TankBase) and c_tank.is_dead:
                                    collide_tanks.pop(i)

                            # 有碰撞时: 放弃刚刚的移动
                            if collide_tanks:
                                is_real_move = False

                            # 判断是否和无阵营实体碰撞(排除 冰，树)
                            for name, group in self.scene.screen_entities.items():
                                if name not in [TypeIdConst.ENTITY_ICE, TypeIdConst.ENTITY_TREE]:
                                    ret = pygame.sprite.spritecollide(tank, group, False, None)
                                    if ret:
                                        is_real_move = False
                                        break
                            # print("next_direct move result:", is_real_move, tank)
                            if is_real_move:
                                # 可以发生移动
                                tank.set_direction(tank.next_direction)
                                tank.set_next_direction(MoveDirection.NONE)
                                tank.start_cool_move_next(self.scene.frame_physical_rate)
                                continue

                        tank.resume_scene()
                    if tank.direction:
                        # 如果当前方向有值
                        # if tank.next_direction:
                        #     print("before cur_dir try:", tank)
                        has_move = tank.try_move_cur(self.scene.frame_physical_rate)
                        # if tank.next_direction:
                        #     print("after cur_dir try: can_move={}, tank={}".format(has_move, tank))
                        if has_move:
                            is_real_move = True
                            collide_tanks = []
                            # 判断是否和异势力坦克碰撞
                            collide_npc = pygame.sprite.spritecollide(tank, other_tanks, False, None)
                            collide_tanks.extend(collide_npc)

                            # 判断是否和同势力其他坦克碰撞
                            same_tanks.remove(tank)
                            collide_friend = pygame.sprite.spritecollide(tank, same_tanks, False, None)
                            same_tanks.add(tank)
                            collide_tanks.extend(collide_friend)

                            # 有碰撞时: 放弃刚刚的移动， 同时碰撞双方操作：
                            # 同方向碰撞（追尾）：被追尾的保持原来速度，追尾的停止移动;
                            # 反方向碰撞：双方停止移动
                            # 垂直方向碰撞：双方停止移动
                            if collide_tanks:
                                is_real_move = False

                                tank.stop_move()  # 停止移动

                                for _tank in collide_tanks:
                                    if isinstance(_tank, TankBase):
                                        ret = MoveDirection.compare_direction(tank.direction, _tank.direction)
                                        if 0 == ret:  # 同方向
                                            if _tank.real_speed >= tank.real_speed:
                                                _tank.stop_move()
                                            else:
                                                tank.stop_move()
                                        else:
                                            _tank.stop_move()
                                            tank.stop_move()

                            # 判断是否和无阵营实体碰撞(排除 冰，树)
                            for name, group in self.scene.screen_entities.items():
                                if name not in [TypeIdConst.ENTITY_ICE, TypeIdConst.ENTITY_TREE]:
                                    ret = pygame.sprite.spritecollide(tank, group, False, None)
                                    if ret:
                                        is_real_move = False
                                        tank.stop_move()
                                        break
                            if is_real_move:
                                # 判断是否和 冰，树发生碰撞
                                ice_group = self.scene.screen_entities[TypeIdConst.ENTITY_ICE]
                                ret = pygame.sprite.spritecollide(tank, ice_group, False, None)
                                if ret:
                                    base_info = universe.get_type_base_info(TypeIdConst.ENTITY_ICE)
                                    abs_digit: float = base_info.extra.get("speed_buff", {}).get("abs_digit", 0)
                                    rel_ratio: float = base_info.extra.get("speed_buff", {}).get("rel_ratio", 0)
                                    duration: int = base_info.extra.get("speed_buff", {}).get("duration", 1) \
                                        * self.scene.gate_data.frame_physical_rate
                                    buff = Buff(source=TypeIdConst.ENTITY_ICE, duration=duration, abs_digit=abs_digit,
                                                rel_ratio=rel_ratio, is_unique=True)
                                    tank.add_buff(buff)
                                tree_group = self.scene.screen_entities[TypeIdConst.ENTITY_TREE]
                                ret = pygame.sprite.spritecollide(tank, tree_group, False, None)
                                if ret:
                                    base_info = universe.get_type_base_info(TypeIdConst.ENTITY_TREE)
                                    abs_digit: float = base_info.extra.get("speed_buff", {}).get("abs_digit", 0)
                                    rel_ratio: float = base_info.extra.get("speed_buff", {}).get("rel_ratio", 0)
                                    duration: int = base_info.extra.get("speed_buff", {}).get("duration", 1) \
                                        * self.scene.gate_data.frame_physical_rate
                                    buff = Buff(source=TypeIdConst.ENTITY_TREE, duration=duration, abs_digit=abs_digit,
                                                rel_ratio=rel_ratio, is_unique=True)
                                    tank.add_buff(buff)
                                # 判断是否和skill碰撞
                                ret = pygame.sprite.spritecollide(tank, self.scene.skills, False, None)
                                if ret:
                                    for r in ret:
                                        if isinstance(r, Skill):
                                            r.pick(self.scene, tank)
                                        self.scene.skills.remove(r)

                            # print("cur direction move result:", is_real_move, tank)
                            if is_real_move:
                                # 可以发生移动
                                continue
                            else:
                                tank.resume_scene()

    def bullet_move_handler(self, faction: Faction) -> None:
        same_bullets: Optional[Group] = None
        other_tanks: Optional[Group] = None
        other_bullets: Optional[Group] = None
        if Faction.OUR_FACTION == faction:
            same_bullets = self.scene.our_bullets
            other_tanks = self.scene.npc_tanks
            other_bullets = self.scene.npc_bullets
        elif Faction.NPC == faction:
            same_bullets = self.scene.npc_bullets
            other_tanks = self.scene.our_tanks
            other_bullets = self.scene.our_bullets

        for bullet in same_bullets:
            if isinstance(bullet, Bullet):
                has_move = bullet.try_move_cur(self.scene.frame_physical_rate)
                if has_move:
                    hurt = bullet.get_hurt()
                    # 判断是否和异势力子弹, 或异势力坦克碰撞
                    for group in [other_bullets, other_tanks]:
                        ret = pygame.sprite.spritecollide(bullet, group, False, None)
                        if ret:
                            for r in ret:
                                if isinstance(r, EntityBase) and not r.is_dead:
                                    r.receive_hurt(hurt_hp=hurt)
                                    if r.can_remove():
                                        group.remove(r)
                                # 承受反伤
                                bullet.receive_anti_hurt()

                    # 判断是否和静态实体碰撞
                    for type_id, _group in self.scene.screen_entities.items():
                        if type_id not in [TypeIdConst.ENTITY_ICE,
                                           TypeIdConst.ENTITY_RIVER,
                                           TypeIdConst.ENTITY_TREE]:
                            ret = pygame.sprite.spritecollide(bullet, _group, False, None)
                            if ret:
                                for r in ret:
                                    if isinstance(r, EntityBase):
                                        r.receive_hurt(hurt_hp=hurt)
                                        if r.can_remove():
                                            _group.remove(r)
                                    # 承受反伤
                                    bullet.receive_anti_hurt()

                    if bullet.can_remove():
                        same_bullets.remove(bullet)

    def move_and_check_collide(self):
        # 移动及碰撞检测

        # 坦克处理
        self._tank_move_handler(Faction.OUR_FACTION)
        self._tank_move_handler(Faction.NPC)

        # 子弹处理
        self.bullet_move_handler(Faction.OUR_FACTION)
        self.bullet_move_handler(Faction.NPC)
